package com.echo.code.thread.aqs;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * ConditionExam Created by baiyap on 2018/10/10.
 */
public class ConditionExam {

  public static void main(String[] args) {
    ExecutorService service = Executors.newCachedThreadPool();
    BoundedBuffer buffer = new BoundedBuffer();
    service.execute(() -> {
      try {
        for (int i = 0; i < 1000; i++) {
          String str = "string" + i;
          buffer.put(str);
          System.out.println(Thread.currentThread().getName() + " put " + str);
          TimeUnit.MILLISECONDS.sleep(50);
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });
    service.execute(() -> {
      try {
        for (int i = 0; i < 1000; i++) {
          String str = (String) buffer.take();
          System.out.println(Thread.currentThread().getName() + " take " + str);
          TimeUnit.MILLISECONDS.sleep(50);
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }

    });
    service.shutdown();
  }

  private static class BoundedBuffer {

    Lock lock = new ReentrantLock();
    Condition notFull = lock.newCondition();
    Condition notEmpty = lock.newCondition();

    Object[] items = new Object[100];
    int putPtr;
    int takePtr;
    int count;

    public void put(Object x) throws InterruptedException {
      lock.lock();
      try {
        while (count == items.length) {
          notFull.await();
        }
        items[putPtr] = x;
        if (++putPtr == items.length) {
          putPtr = 0;
        }
        ++count;
        notEmpty.signal();
      } finally {
        lock.unlock();
      }
    }

    public Object take() throws InterruptedException {
      lock.lock();
      try {
        while (count == 0) {
          notEmpty.await();
        }
        Object x = items[takePtr];
        if (++takePtr == items.length) {
          takePtr = 0;
        }
        --count;
        notFull.signal();
        return x;
      } finally {
        lock.unlock();
      }
    }
  }
}
